package com.deying.httplockserver.core;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.simp.SimpMessagingTemplate;

import com.deying.httplockserver.utils.NettyHttpUtils;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;

/**
 * 加锁，解锁的 处理者
 * 进入处理的就要被标记为ctx当前的lockRequest；返回任何reponse，立即抹除
 * 被加入处理队列，lockRequest就要和worker绑定（重入锁除外）；被unLock 或者 lockTime超时后，解除绑定
 * @author jjliu
 *
 */
public class Worker {
	private final Logger logger = LoggerFactory.getLogger(Worker.class);

	ConcurrentHashMap<String, ArrayList<HttpLockRequest>> requestMap = new ConcurrentHashMap<String, ArrayList<HttpLockRequest>>();
	ScheduledExecutorService  scheduledExecutorService  = Executors.newScheduledThreadPool(1);
	
	public Map<String, ArrayList<HttpLockRequest>> getRequestMap() {
		return requestMap;
	}

	public Worker() {
	}
	
	/**
	 * 添加锁
	 * @param request
	 */
	protected void addLock(HttpLockRequest request){
		if(request.isAlive()){
			//标记为channel当前的lockRequest
			NettyHttpUtils.markCtxBlockHttpLockRequest(request);
			logger.debug("request {}-{} 在ctx上阻塞等待",request.getName(),request.getTid());
			ArrayList<HttpLockRequest> requestList = requestMap.get(request.getName());
			if(requestList==null){
				//成功获得锁
				requestList = new ArrayList<HttpLockRequest>();
				requestMap.put(request.getName(),requestList );
				//标记为被worker控制
				request.bindWorker(this,requestList);
				activityLockList(requestList);
			}else{
				//不是首个锁
				HttpLockRequest oldRequest = requestList.get(0);
				if(oldRequest.getTid().equals(request.getTid())){
					//重入锁
					oldRequest.setReenterNum(oldRequest.getReenterNum()+1);
					NettyHttpUtils.sendSuccess(request.getCtx());
					NettyHttpUtils.unMarkCtxBlockHttpLockRequest(request); // 解除当前channel的lockRequest
					logger.debug("request {}-{} 在ctx上停止阻塞等待",request.getName(),request.getTid());
				}else{
					//非重入锁
					if(request.getWaitTime()>0){
						//TODO 需要判断是否在等待队列里，有相同的tid，为客户端不按规矩，没有等待阻塞，继续发送请求
						//同步等待
						request.bindWorker(this,requestList);
						ScheduledFuture<?> scheduledFuture =startWaitTimeTimer(request, requestList);
						request.setWaitTimer(scheduledFuture); //此时当前channel的lockRequest，长期保持，直到被activityLockList，或者waitTime超时，客户端表现为阻塞，无法新请求。
					}else{
						//try锁直接失败
						NettyHttpUtils.sendErrorTry(request.getCtx());
						NettyHttpUtils.unMarkCtxBlockHttpLockRequest(request); // 解除当前channel的lockRequest
						logger.debug("request {}-{} 在ctx上停止阻塞等待",request.getName(),request.getTid());
					}
				}
			}
		}
	}
	/**
	 * 开启锁竞争等待超时定时器
	 * @param request
	 * @param requestList
	 * @return
	 */
	protected ScheduledFuture<?> startWaitTimeTimer(final HttpLockRequest request,final ArrayList<HttpLockRequest>requestList){
		request.setWaitTimestamp(System.currentTimeMillis());
		ScheduledFuture<?> scheduledFuture = scheduledExecutorService.schedule(new Runnable() {
			@Override
			public void run() {
				if(request.isAlive()){
					//等待超时
					logger.debug("request {}-{} waitTime到期",request.getName(),request.getTid());
					request.unBindWorker(requestList);
					if(requestList.size()==0){
						requestMap.remove(request.getName()); //删除空队列
					}
					NettyHttpUtils.sendErrorTryTimeout(request.getCtx());
					NettyHttpUtils.unMarkCtxBlockHttpLockRequest(request); // 解除当前channel的lockRequest
					logger.debug("request {}-{} 在ctx上停止阻塞等待",request.getName(),request.getTid());
				}
			}
		}, request.getWaitTime(), TimeUnit.MILLISECONDS);
		return scheduledFuture;
	}
	/**
	 * 激活某个锁等待队列
	 * @param requestList
	 */
	protected void activityLockList(final ArrayList<HttpLockRequest> requestList){
		if(requestList.size()>0){
			HttpLockRequest request = requestList.get(0);
			logger.debug("request {}-{} 获得锁",request.getName(),request.getTid());
			request.stopWaitTimer();
			ScheduledFuture<?> scheduledFuture= startLockTimeTimer(request,requestList);
			request.setLockTimer(scheduledFuture);
			NettyHttpUtils.sendSuccess(request.getCtx());
			NettyHttpUtils.unMarkCtxBlockHttpLockRequest(request); // 解除当前channel的lockRequest
			logger.debug("request {}-{} 在ctx上停止阻塞等待",request.getName(),request.getTid());
		}
	}
	/**
	 * 开启锁定超时定时器
	 * @param request
	 * @param requestList
	 * @return
	 */
	private ScheduledFuture<?> startLockTimeTimer(final HttpLockRequest request,final ArrayList<HttpLockRequest>requestList){
		request.setLockedTimestamp(System.currentTimeMillis());
		ScheduledFuture<?> scheduledFuture = scheduledExecutorService.schedule(new Runnable() {
			@Override
			public void run() {
				if(request.isAlive()){
					logger.debug("request {}-{} lockTime到期",request.getName(),request.getTid());
					//锁到期
					request.unBindWorker(requestList);
					if(requestList.size()==0){
						requestMap.remove(request.getName()); //删除空队列
					}else{
						activityLockList(requestList);
					}
				}
			}
		}, request.getLockTime(), TimeUnit.MILLISECONDS);
		return scheduledFuture;
	}
	
	/**
	 * 外部线程锁请求
	 * @param request
	 */
	public void postLock(final HttpLockRequest request){
		scheduledExecutorService.execute(new Runnable() {
			@Override
			public void run() {
				if(request.isAlive()){
					addLock(request);
				}
			}
		});
		
	};
	/**
	 * 外部线程解锁
	 * @param httpLockRelease
	 */
	public void postUnLock(final HttpLockRelease httpLockRelease){
		scheduledExecutorService.execute(new Runnable() {
			@Override
			public void run() {
				ArrayList<HttpLockRequest> requestList = requestMap.get(httpLockRelease.getName());
				if(requestList==null){
					//没有这个锁
					NettyHttpUtils.sendErrorNoLock(httpLockRelease.getCtx());
				}else if(requestList.size()==0){
					//不会发生的情况
					NettyHttpUtils.sendSuccess(httpLockRelease.getCtx());
					logger.error("空白队列没有清空 {}",httpLockRelease.getName());
				}else{
					HttpLockRequest request = requestList.get(0);
					if(request.getTid().equals(httpLockRelease.getTid())){
						if(httpLockRelease.isAcross() ||  request.getReenterNum()<=0){
							request.unBindWorker(requestList);
							NettyHttpUtils.sendSuccess(httpLockRelease.getCtx());
							if(requestList.size()==0){
								requestMap.remove(httpLockRelease.getName());//删除空队列
							}else{
								activityLockList(requestList);
							}
						}else{
							request.setReenterNum(request.getReenterNum()-1);
							NettyHttpUtils.sendSuccess(httpLockRelease.getCtx());
						}
					}else{
						NettyHttpUtils.sendErrorTid(httpLockRelease.getCtx());
					}
				}
			}
		});
	}
	/**
	 * 外部线程执行任务
	 * @param runnable
	 */
	public void postRunnable(Runnable runnable){
		scheduledExecutorService.submit(runnable);
	}
}
